#include <QDateTime>
#include <QHostAddress>

#include "qrawsocket.h"

QRawSocket::QRawSocket(QObject *parent)
    :QAbstractSocket(UnknownSocketType, parent)
{
}

QRawSocket::QRawSocket(int protocol, QObject *parent)
    :QAbstractSocket(UnknownSocketType, parent)
{
    open(protocol);
}

bool QRawSocket::open(int protocol)
{
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
    QSysInfo::machineHostName();        // this initializes ws2_32.dll
#endif

    SOCKET sock = socket(AF_INET, SOCK_RAW, protocol);
    if(INVALID_SOCKET == sock) {
        qDebug() << "create sock failed, errcode:" << WSAGetLastError();
        return false;
    }

    setSocketDescriptor(sock);

    this->protocol = protocol;

    return true;
}

quint32 QRawSocket::sendTo(const char *data, quint32 size, const QString &address)
{
    if (this->state() == QAbstractSocket::UnconnectedState) {
        qDebug() << "The socket is not connected";
        return 0;
    }

    struct sockaddr_in dest = {};

    dest.sin_addr.s_addr = htonl(QHostAddress(address).toIPv4Address());
    dest.sin_family= AF_INET;

    if (sendto(socketDescriptor(), data, size, 0, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
        qDebug() << "sendto failed";
        return 0;
    }

    return size;
}

quint32 QRawSocket::sendIcmpEcho(const QString &address, quint16 id, quint16 sequence, quint16 size)
{
    static quint8 buf[65535];
    struct icmphdr *icmph = (struct icmphdr *)buf;

    icmph->type = ICMP_ECHO;
    icmph->un.echo.id = htons(id);
    icmph->un.echo.sequence = htons(sequence);
    icmph->checksum = 0;

    if (size < 8)
        size = 8;

    *((quint64 *)(buf + sizeof(struct icmphdr))) = QDateTime::currentMSecsSinceEpoch();

    icmph->checksum = checkSum((quint16 *)icmph, sizeof(struct icmphdr) + size);

    sendTo((const char *)icmph, sizeof(struct icmphdr) + size, address);

    return sizeof(struct icmphdr) + size;
}

quint16 QRawSocket::checkSum(const quint16 *data, int size)
{
    quint32 cksum = 0;

    while (size > 1) {
        cksum += *data++;
         size -= sizeof(quint16);
    }

    if (size)
        cksum += *(quint8 *)data;

    cksum = (cksum >> 16) + (cksum & 0xffff);
    cksum += (cksum >> 16);

    return (quint16)(~cksum);
}
